package com.agentframework.examples.jinhua;

import com.agentframework.core.agent.AbstractAgent;
import com.agentframework.core.annotation.Agent;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.stringtemplate.v4.ST;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author wangjun
 **/
@Agent(
    name = "jinhuaPlayerAgent",
    scope = Agent.Scope.PROTOTYPE,
    actions = { PlayerAction.class }
)
public class JinhuaPlayerAgent extends AbstractAgent {

    String profile = """
            诈金花（三张牌）是一种扑克游戏，游戏由多轮组成，每轮独立进行，你现在是诈金花游戏中的一位玩家，目标是通过策略成为最终赢家。
            
            你的游戏数据如下： \n
                 "你的唯一标识ID": <playerId>,
                 "你当前手牌": <cards>,
                 "是否已查看手牌": <viewed>,
                 "当前轮下注总金额": <currentBet>,
                 "你当前余额": <balance>,
                 "本轮是否存活": <active>,
                 "累计输赢金额": <totalWinLoss>
            
            其他玩家信息如下： \n
            <otherPlayers:{p | - ID: <p.playerId>, 本轮是否看牌: <p.viewed>, 本轮是否存活: <p.active> \\n}>
            
            游戏流程如下： \n
             **每轮游戏流程**：
            1. **发牌**：荷官为所有玩家发放三张暗牌。
            2. **环形行动**：玩家按顺序轮流行动，可选择下注(跟注或加注)、看牌、比牌或弃牌，行动之后请等待荷官指引。
            3. **结算**：当满足本轮结束条件时（只剩一名未弃牌玩家），荷官宣布本轮结果，淘汰输家。
            
            **整个游戏结束条件**：
            - 达到预设轮数上限（如10轮）。
            - 只剩一名玩家（提前结束所有轮次）。
            
            其他规则：
            - 牌型由大到小：豹子 > 顺金 > 金花 > 顺子 > 对子 > 单张。
            - 蒙牌下注（不看牌）最低5倍，看牌后下注最低10倍，加注需大于当前最高下注。
            - 比牌时输家淘汰，弃牌玩家退出本轮但保留历史下注。
            
            **当前轮次操作**（每轮可多次行动）：
            - "看牌"：查看手牌，后续下注需至少是前一个玩家下注金额的翻倍（如果前一个玩家是蒙牌下注的情况下）。
            - "下注 X倍"：跟随前一位玩家的下注金额（X为当前基准金额，也可提高下注至X倍（X需>当前最高下注））。
            - "比牌 玩家ID"：向指定玩家发起比牌（如"比牌 e9fc1d4d(玩家实际ID)"）。
            - "弃牌"：退出本轮，失去所有已下注筹码。
            
            **注意事项**：
            - 你下注的时候，必须要指定下注金额是多少，而且你需要根据玩家是否看牌下注，来判断玩家下注是否正确，比如上一个玩家看牌下注10倍，那么蒙牌下注只需要5倍或者大于5倍，如果是看牌下注，必须不能低于10倍
            - 本轮结束后，若游戏未终止，荷官将重新发牌开始下一轮。
            - 你无法查看其他玩家的牌，但可通过下注行为推测其策略（如频繁蒙牌可能手牌较弱）。
            - 看牌之后不能再重复看牌，一轮游戏只能看牌一次，直到本轮游戏结束之后，下轮游戏才能看牌。
            - 比牌的时候，你想跟谁比完全由你任意挑选一个玩家，你想跟任意一个还在玩的玩家比牌都是被允许的，但是特别注意，弃牌或者被淘汰的玩家不能进行比牌。
            """;

    public JinhuaPlayerAgent(JinhuaTool jinhuaTool) {
        super("JinhuaGamePlayer");
        this.agentProfile = profile;
//        setToolCallbacks(jinhuaTool);
    }

    @Override
    protected AssistantMessage react() {
        synchronized (JinhuaPlayerAgent.class) {
            context.setTodo(getActions().get(0));
            return act();
        }
    }

    @Override
    protected int observe() throws InterruptedException {

        int messageCount = super.observe();

        // 淘汰者不再参与游戏
        if (!isActive()) {
            logger.info("agent {} - {}, 淘汰，不再处理消息", agentName, agentId);
            return 0;
        }

        if (isGameOver()) {
            logger.debug("游戏已经结束，不再处理消息");
            return 0;
        }

//        if (messageCount > 0) {
//            Message message = getLatestMessage();
//            logger.debug(">>>玩家 {} process getLatestMessage message: {}", getAgentId(), message);
//        }

        return messageCount;
    }

    private boolean isActive() {
        return getJinhuaGameState().getPlayer(getAgentId()).isActive();
    }

    private boolean isGameOver() {
        return getJinhuaGameState().isGameOver();
    }

    /**
     * "玩家唯一标识ID": {playerId},
        "玩家当前手牌": {cards},
        "是否已查看手牌": {viewed},
        "当前轮下注金额": {currentBet},
        "玩家余额": {balance},
        "是否参与本轮": {active},
        "累计输赢金额": {totalWinLoss}
     * @return ""
     */
    @Override
    public String getAgentProfile() {
        JinhuaGameState jinhuaGameState = getJinhuaGameState();
        PlayerInfo playerInfo = jinhuaGameState.getPlayer(getAgentId());
        String template = this.agentProfile;

        ST st = new ST(template);

        st.add("playerId", playerInfo.getPlayerId());
        st.add("viewed", playerInfo.isViewed());
        st.add("active", playerInfo.isActive());
        st.add("cards", "");
        st.add("currentBet", playerInfo.getCurrentBet());
        st.add("balance", playerInfo.getBalance());
        st.add("totalWinLoss", playerInfo.getTotalWinLoss());

        if (playerInfo.isViewed()) {
            st.add("cards", String.join(",", playerInfo.getCards()));
        }

        List<OtherPlayer> otherPlayers = jinhuaGameState.getPlayerList().stream()
                .filter(p -> !p.getPlayerId().equals(getAgentId()))
                .map(p -> new OtherPlayer(
                        p.getPlayerId(),
                        p.isViewed(),
                        p.isActive()))
                .collect(Collectors.toList());

        st.add("otherPlayers", otherPlayers);

        return st.render();
    }

    private JinhuaGameState getJinhuaGameState() {
        return ((JinhuaEnvironment) getEnvironment()).getJinhuaGameState();
    }

    public static class OtherPlayer {
        String playerId;
        boolean viewed;
        boolean active;

        public OtherPlayer(String playerId, boolean viewed, boolean active) {
            this.playerId = playerId;
            this.viewed = viewed;
            this.active = active;
        }

        public String getPlayerId() {
            return playerId;
        }

        public void setPlayerId(String playerId) {
            this.playerId = playerId;
        }

        public boolean isViewed() {
            return viewed;
        }

        public void setViewed(boolean viewed) {
            this.viewed = viewed;
        }

        public boolean isActive() {
            return active;
        }

        public void setActive(boolean active) {
            this.active = active;
        }
    }

}
